package main

import (
	"errors"
	"flag"
	"fmt"
	"github.com/BurntSushi/toml"
	"go.einride.tech/can"
	"go.einride.tech/can/pkg/candevice"
	"go.einride.tech/can/pkg/socketcan"
	"golang.org/x/net/context"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"
)

// CanDevConfig 结构体用于映射配置文件的内容
type CanDevConfig struct {
	Name    string
	Bitrate uint32
}

// TcpSrvConfig 结构体用于映射配置文件的内容
type TcpSrvConfig struct {
	Addr string
	Port uint32
}

// GlobalConfig 结构体用于映射配置文件的内容
type GlobalConfig struct {
	Can CanDevConfig
	Tcp TcpSrvConfig
}

// TcpClient Tcp客户端
type TcpClient struct {
	conn       net.Conn
	timeout    time.Duration
	activeTime time.Time
	sendChan   chan string
}

// Message 消息
type Message struct {
	source  interface{}
	payload string
}

// 全局变量
var (
	globalConfig = GlobalConfig{
		Can: CanDevConfig{Name: "can0", Bitrate: uint32(10000)},
		Tcp: TcpSrvConfig{Addr: "localhost", Port: 1903},
	} // 默认配置参数
	clients      = make(map[net.Conn]*TcpClient) // TCP 客户端连接池
	clientsMutex sync.Mutex                      // 互斥锁保护连接池
	canSendChan  = make(chan Message, 1024)      // CAN 数据发送通道
	canRecvChan  = make(chan Message, 1024)      // CAN 数据接收通道
	tcpSendChan  = make(chan Message, 1024)      // TCP 数据发送通道
	tcpRecvChan  = make(chan Message, 1024)      // TCP 数据接收通道
)

// 打开CAN设备
func openCanDev(name string, bitrate uint32) (d *candevice.Device, err error) {
	// 创建设备
	d, err = candevice.New(name)
	if err != nil {
		return nil, err
	}
	// 关闭设备
	err = d.SetDown()
	if err != nil {
		return nil, err
	}
	// 设置参数
	if !strings.HasPrefix(name, "vcan") {
		err = d.SetBitrate(bitrate)
		if err != nil {
			return nil, err
		}
	}
	// 打开设备
	err = d.SetUp()
	if err != nil {
		return nil, err
	}
	// 返回设备
	return d, nil
}

// 关闭CAN设备
func closeCanDev(d *candevice.Device) {
	if d != nil {
		isUp, err := d.IsUp()
		if err != nil && isUp {
			_ = d.SetDown()
		}
	}
}

// CAN设备数据发送
func sendCanDev(d *candevice.Device, conn net.Conn, done chan bool) {
	transmitter := socketcan.NewTransmitter(conn)
	defer func(transmitter *socketcan.Transmitter) {
		_ = transmitter.Close()
	}(transmitter)
	for {
		select {
		case <-done:
			log.Println("【CanDev】发送终止")
			return
		default:
			message := <-canSendChan
			frame := can.Frame{}
			err := frame.UnmarshalString(message.payload)
			if err != nil {
				log.Printf("【CanDev】无效数据：%v", message.payload)
				continue
			}

			_ = transmitter.TransmitFrame(context.Background(), frame)

			// 因为自己发送的数据自己收不到，所以将数据发送总线后立即投递回canRecvChan方便观察通信细节
			canRecvChan <- Message{source: d, payload: frame.String()}
			log.Println("【CanDev】收到数据：" + frame.String())
		}
	}
}

// CAN设备数据接收
func recvCanDev(d *candevice.Device, conn net.Conn, done chan bool) {
	receiver := socketcan.NewReceiver(conn)
	defer func(receiver *socketcan.Receiver) {
		_ = receiver.Close()
	}(receiver)
	for {
		select {
		case <-done:
			log.Println("【CanDev】接收终止")
			return
		default:
			ok := receiver.Receive()
			if ok {
				frame := receiver.Frame()
				canRecvChan <- Message{source: d, payload: frame.String()}
				log.Println("【CanDev】收到数据：" + frame.String())
			}
		}
	}
}

// CAN设备服务
func serveCan(ctx context.Context) {
	name := globalConfig.Can.Name
	bitrate := globalConfig.Can.Bitrate
	d, err := openCanDev(name, bitrate)
	defer closeCanDev(d)
	if err != nil {
		if errors.Is(err, os.ErrPermission) {
			log.Fatalf("【CanDev】启动失败，权限不足：%v,%v", name, bitrate)
		} else {
			log.Fatalf("【CanDev】启动失败，未知异常：%v,%v", name, bitrate)
		}
	}
	log.Printf("【CanDev】设备启动：%v,%v", name, bitrate)
	conn, err := socketcan.DialContext(context.Background(), "can", name)
	if err != nil {
		log.Fatalf("%v", err)
	}
	log.Printf("【CanDev】连接成功：%v,%v", name, bitrate)
	sendDone := make(chan bool)
	defer close(sendDone)
	go sendCanDev(d, conn, sendDone)
	recvDone := make(chan bool)
	defer close(recvDone)
	go recvCanDev(d, conn, recvDone)
	for {
		select {
		case <-ctx.Done():
			log.Printf("【CanDev】服务结束：%v,%v", name, bitrate)
			return
		default:
			isUp, err := d.IsUp()
			if err != nil {
				panic(err)
			}
			if !isUp {
				sendDone <- true
				recvDone <- true
				break
			}
			time.Sleep(1 * time.Second)
		}
	}
}

// CAN设备设置
func setupCanDev(ctx context.Context, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			return
		default:
			serveCan(ctx)
		}
	}
}

// 处理客户端连接
func handleTcpClient(ctx context.Context, client *TcpClient) {
	defer func() {
		clientsMutex.Lock()
		delete(clients, client.conn)
		clientsMutex.Unlock()
		close(client.sendChan)
		_ = client.conn.Close()
	}()

	// 设置超时时间
	client.timeout = 1 * time.Minute
	// 设置活跃时间
	client.activeTime = time.Now()
	// 创建一个带取消功能的上下文
	clientCtx, clientCancel := context.WithCancel(context.Background())

	// 启动接收
	recvOver := false
	recvDone := make(chan bool)
	go recvTcpConn(client, clientCtx, recvDone)

	// 启动发送
	sendOver := false
	sendDone := make(chan bool)
	go sendTcpConn(client, clientCtx, sendDone)

	for {
		select {
		case <-recvDone:
			recvOver = true
			clientCancel()
		case <-sendDone:
			sendOver = true
			clientCancel()
		case <-time.After(1 * time.Second):
			if recvOver && sendOver {
				log.Printf("【TcpDev】连接关闭：%v", client.conn.RemoteAddr())
				return
			}
			if client.activeTime.Add(client.timeout).Before(time.Now()) {
				log.Printf("【TcpDev】连接超时：%v", client.conn.RemoteAddr())
				clientCancel()
				return
			}
		case <-ctx.Done():
			log.Printf("【TcpDev】服务结束：%v", client.conn.RemoteAddr())
			clientCancel()
			return
		}
	}
}

// TCP连接数据接收
func recvTcpConn(client *TcpClient, ctx context.Context, done chan bool) {
	defer func() {
		done <- true
	}()
	buf := make([]byte, 1024)
	for {
		select {
		case <-ctx.Done():
			log.Printf("【TcpDev】接收结束：%v", client.conn.RemoteAddr())
			return
		default:
			err := client.conn.SetReadDeadline(time.Now().Add(5 * time.Second))
			if err != nil {
				return
			}
			n, err := client.conn.Read(buf)
			if err != nil {
				var netErr net.Error
				var netOpErr *net.OpError
				if errors.As(err, &netErr) && netErr.Timeout() {
					// 超时以后继续尝试
					// log.Printf("【TcpDev】读取超时：%v", client.conn.RemoteAddr())
					continue
				} else if errors.Is(err, io.EOF) {
					log.Printf("【TcpDev】连接断开：%v", client.conn.RemoteAddr())
					log.Printf("【TcpDev】接收结束：%v", client.conn.RemoteAddr())
					return
				} else if errors.As(err, &netOpErr) && netOpErr.Err.Error() == "use of closed network connection" {
					// 判断是否是因为连接关闭引起的错误
					log.Printf("【TcpDev】连接关闭：%v", client.conn.RemoteAddr())
					log.Printf("【TcpDev】接收结束：%v", client.conn.RemoteAddr())
					return
				} else {
					// 其他异常情况
					log.Printf("【TcpDev】接收异常：%v, %v", client.conn.RemoteAddr(), err)
					log.Printf("【TcpDev】接收结束：%v", client.conn.RemoteAddr())
					return
				}
			}
			if n > 0 {
				client.activeTime = time.Now()
				text := string(buf[:n])
				normalize := strings.Replace(text, "\r\n", "\n", -1)
				liens := strings.Split(normalize, "\n")
				for _, line := range liens {
					data := strings.TrimSpace(line)
					if data == "" {
						continue
					}
					if strings.Contains(data, "#") && len(strings.Split(data, "#")) == 2 {
						if len(data) > 1 {
							log.Printf("【TcpDev】收到数据：%s, %v", client.conn.RemoteAddr(), data)
							tcpRecvChan <- Message{source: client.conn, payload: data}
						}
					} else {
						// 普通消息
						if data == "ping" {
							log.Printf("【TcpDev】心跳信号：%s, %v", client.conn.RemoteAddr(), data)
							tcpSendChan <- Message{source: "canova", payload: "pong"}
							// _, err := client.conn.Write([]byte("pong\n"))
							// if err != nil {
							//	return
							//}
						}
					}
				}
			}
		}
	}
}

// TCP连接数据发送
func sendTcpConn(client *TcpClient, ctx context.Context, done chan bool) {
	defer func() {
		done <- true
	}()
	for {
		select {
		case <-ctx.Done():
			log.Printf("【TcpDev】发送结束：%v", client.conn.RemoteAddr())
			return
		case s := <-client.sendChan:
			log.Printf("【TcpDev】发送数据：%v, %v", client.conn.RemoteAddr(), s)
			_, err := client.conn.Write([]byte(s + "\n"))
			if err != nil {
				log.Printf("【TcpDev】发送错误：%v", client.conn.RemoteAddr())
				log.Printf("【TcpDev】发送结束：%v", client.conn.RemoteAddr())
				return
			}
		}
	}
}

// TCP监听服务
func serveTcp(ctx context.Context) {
	addr := globalConfig.Tcp.Addr
	port := globalConfig.Tcp.Port
	// 启动TCP监听
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port))
	// 退出关闭监听
	defer func(listener net.Listener) {
		_ = listener.Close()
	}(listener)
	if err != nil {
		log.Printf("【TcpSrv】端口占用：%s:%d", addr, port)
		return
	}
	log.Printf("【TcpSrv】启动监听：%s:%d", addr, port)
	for {
		select {
		case <-ctx.Done():
			log.Printf("【TcpSrv】服务结束：%s:%d", addr, port)
			return
		default:
			err := listener.(*net.TCPListener).SetDeadline(time.Now().Add(1 * time.Second))
			if err != nil {
				log.Printf("【TcpSrv】配置失败：%s:%d", addr, port)
				return
			}
			conn, err := listener.Accept()
			if err != nil {
				var netErr net.Error
				if errors.As(err, &netErr) && netErr.Timeout() {
					continue
				} else {
					return
				}
			}

			log.Printf("【TcpSrv】新的连接: %s", conn.RemoteAddr())

			clientsMutex.Lock()
			clients[conn] = &TcpClient{conn: conn, sendChan: make(chan string, 1024)}
			clientsMutex.Unlock()

			go handleTcpClient(ctx, clients[conn])
		}
	}
}

// TCP监听设置
func setupTcpSrv(ctx context.Context, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			return
		default:
			serveTcp(ctx)
			time.Sleep(1 * time.Second)
		}
	}
}

// 数据分发服务配置
func setupDistSrv(ctx context.Context, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			return
		case s := <-canRecvChan: // can收到的数据路由到tcp发送通道
			tcpSendChan <- s
		case s := <-tcpRecvChan: // tcp收到的数据路由到can发送通道
			canSendChan <- s
			tcpSendChan <- s
		case s := <-tcpSendChan: // tcp发送通道收到的数据分发到所有连接的客户端
			for conn := range clients {
				if conn != s.source {
					clients[conn].sendChan <- s.payload
				}
			}
		}
	}
}

// 主函数
func main() {
	// 定义标志变量
	var (
		name    = flag.String("name", "", "CAN接口名称")
		bitrate = flag.Uint("bitrate", 0, "CAN工作速率")
		addr    = flag.String("addr", "", "TCP监听地址")
		port    = flag.Uint("port", 0, "TCP监听端口")
		config  = flag.String("config", "", "配置文件路径")
	)

	// 解析命令行
	flag.Parse()

	// 加载配置文件
	if len(*config) > 0 {
		_, err := toml.DecodeFile(*config, &globalConfig)
		if err != nil {
			log.Fatalln("【MainRT】加载配置文件失败")
		}
	}

	// 使用命令行参数覆盖默认参数和配置文件参数
	if name != nil && len(*name) > 0 {
		globalConfig.Can.Name = *name
	}
	if bitrate != nil && *bitrate > 0 {
		globalConfig.Can.Bitrate = uint32(*bitrate)
	}
	if addr != nil && len(*addr) > 0 {
		globalConfig.Tcp.Addr = *addr
	}
	if port != nil && *port > 0 {
		globalConfig.Tcp.Port = uint32(*port)
	}

	// 创建一个带取消功能的上下文
	ctx, cancel := context.WithCancel(context.Background())

	// 创建一个等待组
	var wg sync.WaitGroup

	// 启动CanDev
	wg.Add(1)
	go setupCanDev(ctx, &wg)

	// 启动TcpSrv
	wg.Add(1)
	go setupTcpSrv(ctx, &wg)

	// 启动分发服务
	wg.Add(1)
	go setupDistSrv(ctx, &wg)

	// 在单独的goroutine中等待信号
	go func() {
		// 注册信号处理
		sigChan := make(chan os.Signal, 1)
		signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
		sig := <-sigChan
		log.Printf("【SigMon】退出信号: %v", sig)
		// 发送终止信号给所有工作线程
		cancel()
	}()

	// 等待所有工作线程退出
	wg.Wait()

	// 打印退出日志
	log.Println("【MainRT】程序退出")
}
